<?php
// Autor (c) Miroslav Novak, www.platiti.cz
// Pouzivani bez souhlasu autora neni povoleno
// #Ver:PRV043#

//error_reporting(E_ALL);

require_once(dirname(__FILE__)."/UniModul.php");
require_once(dirname(__FILE__)."/PayuGate.php");

class UniPayUConfig {
	public $gwOrderNumberOffset;
	public $url_payu;
	
	public $supportedCurrency;
	public $pos_id;
	public $pos_auth_key;
	public $key1;
	public $key2;

	public $supportedCurrency_pos2;
	public $pos_id_pos2;
	public $pos_auth_key_pos2;
	public $url_payu_pos2;
	public $key1_pos2;
	public $key2_pos2;

	public $convertToCurrencyIfUnsupported; //jedna mena
	public $subMethodsSelection; //array submetod
}


class UniPayU extends UniModul {
	
	protected $config;
	
	public function __construct($configSetting, $subMethod) {
		parent::__construct("PayU", $configSetting, $subMethod);
		$this->setConfigFromData($configSetting);
	}
	
	public function setConfigFromData($configSetting) {
		$this->config = new UniPayUConfig();
		if ($configSetting != null && $configSetting->configData != null) {
			$configData = $configSetting->configData;
			$this->config->url_payu = $configData['url_payu'];

			$this->config->supportedCurrency = $configData['supportedCurrency'];
			$this->config->pos_id = $configData['pos_id'];
			$this->config->pos_auth_key = $configData['pos_auth_key'];
			$this->config->key1 = $configData['key1'];
			$this->config->key2 = $configData['key2'];
			if ($this->config->supportedCurrency == '') { $this->config->supportedCurrency = 'CZK'; }  // pro jiz nainstalovane moduly predchozi verze jen s jednim POSem

			$this->config->supportedCurrency_pos2 = $configData['supportedCurrency_pos2'];
			$this->config->pos_id_pos2 = $configData['pos_id_pos2'];
			$this->config->pos_auth_key_pos2 = $configData['pos_auth_key_pos2'];
			$this->config->key1_pos2 = $configData['key1_pos2'];
			$this->config->key2_pos2 = $configData['key2_pos2'];

			$this->config->gwOrderNumberOffset = $configData['gwOrderNumberOffset'];
			$this->config->url_payu = $configData['url_payu'];
			$this->config->convertToCurrencyIfUnsupported = $configSetting->configData['convertToCurrencyIfUnsupported'];
			$this->config->subMethodsSelection = explode(' ',$configSetting->configData['subMethodsSelection']);
		}
	}

	public function getConfigInfo($language='en') {

		$d = $this->dictionary;
		$d->setDefaultLanguage($language);
		

		$configInfo = new ConfigInfo();
		
		$configFields = array();

		$configFields[] = create_initialize_object('ConfigField', array('name'=>'supportedCurrency', 'label'=>$d->get('supportedCurrency'), 'type'=>ConfigFieldType::$choice, 'choiceItems' => array('CZK'=>'CZK', 'EUR'=>'EUR')));
		$configFields[] = create_initialize_object('ConfigField', array('name'=>'pos_id', 'label'=>$d->get('pos_id'), 'type'=>ConfigFieldType::$text));
		$configFields[] = create_initialize_object('ConfigField', array('name'=>'key1', 'label'=>$d->get('key1'), 'type'=>ConfigFieldType::$text));
		$configFields[] = create_initialize_object('ConfigField', array('name'=>'key2', 'label'=>$d->get('key2'), 'type'=>ConfigFieldType::$text));
		$configFields[] = create_initialize_object('ConfigField', array('name'=>'pos_auth_key', 'label'=>$d->get('pos_auth_key'), 'type'=>ConfigFieldType::$text));

		$configFields[] = create_initialize_object('ConfigField', array('name'=>'supportedCurrency_pos2', 'label'=>$d->get('supportedCurrency_pos2'), 'type'=>ConfigFieldType::$choice, 'choiceItems' => array(''=>'---', 'CZK'=>'CZK', 'EUR'=>'EUR')));
		$configFields[] = create_initialize_object('ConfigField', array('name'=>'pos_id_pos2', 'label'=>$d->get('pos_id_pos2'), 'type'=>ConfigFieldType::$text));
		$configFields[] = create_initialize_object('ConfigField', array('name'=>'key1_pos2', 'label'=>$d->get('key1_pos2'), 'type'=>ConfigFieldType::$text));
		$configFields[] = create_initialize_object('ConfigField', array('name'=>'key2_pos2', 'label'=>$d->get('key2_pos2'), 'type'=>ConfigFieldType::$text));
		$configFields[] = create_initialize_object('ConfigField', array('name'=>'pos_auth_key_pos2', 'label'=>$d->get('pos_auth_key_pos2'), 'type'=>ConfigFieldType::$text));

		$configFields[] = create_initialize_object('ConfigField', array('name'=>'url_payu', 'label'=>$d->get('url_payu'), 'type'=>ConfigFieldType::$text));
		
		$configField = new ConfigField();
		$configField->name = 'gwOrderNumberOffset';
		$configField->label = $d->get('gwOrderNumberOffset');
		$configField->type = ConfigFieldType::$text;
		$configFields[]=$configField;

		$configField = new ConfigField();
		$configField->name = 'convertToCurrencyIfUnsupported';
		$configField->label = $d->get('convertToCurrencyIfUnsupported');
		$configField->type = ConfigFieldType::$text;
		$configFields[]=$configField;

		
		$configField = new ConfigField();
		$configField->name = 'orderStatusSuccessfull';
		$configField->label = $d->get('orderStatusSuccessfull');
		$configField->type = ConfigFieldType::$orderStatus;
		$configFields[]=$configField;

		$configField = new ConfigField();
		$configField->name = 'orderStatusPending';
		$configField->label = $d->get('orderStatusPending');
		$configField->type = ConfigFieldType::$orderStatus;
		$configFields[]=$configField;

		$configField = new ConfigField();
		$configField->name = 'orderStatusFailed';
		$configField->label = $d->get('orderStatusFailed');
		$configField->type = ConfigFieldType::$orderStatus;
		$configFields[]=$configField;

		$configField = new ConfigField();
		$configField->name = 'subMethodsSelection';
		$configField->label = $d->get('subMethodsSelection');
		$configField->type = ConfigFieldType::$subMethodsSelection;
		$configField->choiceItems = array('main'=>'');  //povolime obecnou submetodu
		$configFields[]=$configField;
		
		$configInfo->configFields = $configFields;
		return $configInfo;
	}

	protected $subMethods = array('Test'=>'t', 'CreditCard'=>'c', 'Mobito'=>'mo', 'SuperCash'=>'sc', 'CeskaSporitelna'=>'cs', 'MPenize'=>'mp', 'MojePlatba'=>'kb', 'EPlatby'=>'rf', 'GEMoney'=>'pg', 'SberBank'=>'pv', 'FioBank'=>'pf', 'Prevod'=>'bt', 'Slozenka'=>'pt');
	
	public function getSubMethods() {
		return array_keys($this->subMethods);
	}
	
	public function queryPrePayGWInfo($orderToPayInfo) {
		if ($orderToPayInfo->subMethod==null) {  //fix pro subModuly
			$orderToPayInfo->subMethod = $this->subMethod;
		}
		$currencySupported = $this->config->supportedCurrency == $orderToPayInfo->currency || $this->config->supportedCurrency_pos2 == $orderToPayInfo->currency;
		list($isPossible, $newcur, $newtotal, $forexMessage, $forexNote, $orderReplyStatusFail) = $this->fixCurrency($orderToPayInfo, $currencySupported);

		$d = $this->dictionary;
		$prePayGWInfo = new PrePayGWInfo();
		$methodNameKey = ($orderToPayInfo->subMethod == '') ? 'payment_method_name' : 'submethod_name_'.$orderToPayInfo->subMethod;
		$prePayGWInfo->paymentMethodName = $this->dictionary->get($methodNameKey, $orderToPayInfo->language);
		$prePayGWInfo->isPossible = $isPossible;
		$prePayGWInfo->forexMessage = $forexMessage;
		if ($orderToPayInfo->subMethod != null) {
			$prePayGWInfo->subMethods = array($orderToPayInfo->subMethod);
		} else {
			$prePayGWInfo->subMethods = $this->config->subMethodsSelection;
		}
		return $prePayGWInfo;
	}

	
	function gatewayOrderRedirectAction($orderToPayInfo) {
		$this->logger->writeLog("NEW_ORDER");
		if ($orderToPayInfo->subMethod==null) {  //fix pro subModuly
			$orderToPayInfo->subMethod = $this->subMethod;
		}
		$currencySupported = $this->config->supportedCurrency == $orderToPayInfo->currency || $this->config->supportedCurrency_pos2 == $orderToPayInfo->currency;
		list($isPossible, $newcur, $newtotal, $forexMessage, $forexNote, $orderReplyStatusFail) = $this->fixCurrency($orderToPayInfo, $currencySupported);
		
		$amount = round($newtotal * 100);
		
		$uniModulData = array('currency'=>$orderToPayInfo->currency);
		
		if (!$isPossible) {
			$transactionPK = $this->writeOrderToDb($orderToPayInfo->shopOrderNumber, $orderToPayInfo->shopPairingInfo, null, $forexNote, $orderReplyStatusFail->orderStatus, null, $orderToPayInfo->uniAdapterData, $uniModulData);
			$this->logger->writeLog("CANNOT SEND ORDER ".print_r($orderToPayInfo, true)."   resultText:".$orderReplyStatusFail->resultText);
			
			$redirectActionFail = new RedirectAction();
			$redirectActionFail->orderReplyStatus = $orderReplyStatusFail;
			return $redirectActionFail;
		} 

		$transactionPK = $this->writeOrderToDb($orderToPayInfo->shopOrderNumber, $orderToPayInfo->shopPairingInfo, null, $forexNote, null, $orderToPayInfo->uniAdapterData, $uniModulData);
		$gwOrderNumber = $transactionPK + $this->config->gwOrderNumberOffset;
		$this->updateGwOrderNumber($transactionPK, $gwOrderNumber);

		$description = $orderToPayInfo->description;
		$description = mb_substr($description, 0, 50, 'UTF-8');

		$payuConfig = $this->getPayuConfig($orderToPayInfo->currency);
		$payu = new PayuGate($payuConfig);
		
		$payargs = array(
			'session_id'=>	$gwOrderNumber,
			'amount' 	=>	$amount,
			'desc'		=>	$description,
			'first_name'=>	$orderToPayInfo->customerData->first_name,
			'last_name'	=>	$orderToPayInfo->customerData->last_name,
			'email'		=>	$orderToPayInfo->customerData->email,
			'order_id'	=>	$orderToPayInfo->shopOrderNumber,
			
			// nepovinne
			'language'	=>	$orderToPayInfo->language,
			'street'	=>	$orderToPayInfo->customerData->street,
			'street_hn'	=>	$orderToPayInfo->customerData->houseNumber,
			'city'	=>	$orderToPayInfo->customerData->city,
			'post_code'	=>	$orderToPayInfo->customerData->post_code,
			'phone'	=>	$orderToPayInfo->customerData->phone,

			// TODO
			//	$country;    muselo by se ejak kontrolovat zatim nepouziju http:www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html)	
		);
		
		if ($orderToPayInfo->subMethod != null && $orderToPayInfo->subMethod != '') {
			$payargs['pay_type'] = $this->subMethods[$orderToPayInfo->subMethod];
		}
		
		$redirectHtml = $payu->sendOrderHtml($payargs);

		$redirectAction = new RedirectAction();
		$redirectAction->redirectForm = $redirectHtml;
		return $redirectAction;
	}
	
	public function gatewayReceiveReply($language='en') {
		$this->logger->writeLog("REPLY   ".$_SERVER["QUERY_STRING"]);

		$gwOrderNumber_pre = $_GET['session_id'];
		$transactionRecord_pre = $this->getOrderTransactionRecordFromDb(null, $gwOrderNumber_pre);
		$payuConfig = $this->getPayuConfig($transactionRecord_pre->uniModulData['currency']);
		$payu = new PayuGate($payuConfig);
		
		$result = $payu->receiveReply($trans_data0);
		$tmguard = time();
		while(true) {
			$orderReplyStatus = $this->callAndProcessPayUGet($payu, $trans_data0['session_id'], $result!=0, false);
			if (!in_array($orderReplyStatus->orderStatus, array(OrderStatus::$pending, OrderStatus::$invalidReply)) || time() - $tmguard >= 5) break;  
			//opakovane dotazovani. Stav 1 (prekladan kvuli mazani kosiku ve VM2 jako invalid reply) se  pri platbe kartou se preklopi nekdy az kratce po reply, takze se pokusim zjistit vysledek synchronne po dobu 5s
			sleep(1); // dalsi dotaz delam po jedne sekunde
		};
		
		// hlášky pro časté nesystémové chyby při překročení min a max částek pro zvolený způsob platby
		if ($result == 205) {
			$orderReplyStatus->resultText = $this->dictionary->get('errmsg_amountTooSmall', $language);
		} else if ($result == 206) {
			$orderReplyStatus->resultText = $this->dictionary->get('errmsg_amountTooBig', $language);
		}
			
		$this->logger->writeLog("orderReplyStatus=".$orderReplyStatus->orderStatus);
		return $orderReplyStatus;
	}

	function gatewayReceiveNotification() {
		$gwOrderNumber_pre = $_POST['session_id'];
		$transactionRecord_pre = $this->getOrderTransactionRecordFromDb(null, $gwOrderNumber_pre);
		$payuConfig = $this->getPayuConfig($transactionRecord_pre->uniModulData['currency']);
		$payu = new PayuGate($payuConfig);
		
		$result = $payu->receiveNotification($session_id);
		$this->logger->writeLog("NOTIFY   ".$_SERVER["QUERY_STRING"]);
		$orderReplyStatus = $this->callAndProcessPayUGet($payu, $session_id, $result!=0, true);
		$payu->closeNotification("OK");
		$this->logger->writeLog("orderReplyStatus=".$orderReplyStatus->orderStatus);
		return $orderReplyStatus;
	}
	
	
	function callAndProcessPayUGet($payu, $gwOrderNumber, $pre_error, $isNotification) {
		BeginSynchronized();
		$transactionRecord = $this->getOrderTransactionRecordFromDb(null, $gwOrderNumber);
		if ($pre_error) {
			if ($isNotification) {
				$orderStatus = OrderStatus::$invalidReply;  // spatna notifikace k ignorovani
			} else {
				$orderStatus = OrderStatus::$failedRetriable;  // v reply je to failedRetriable i kdyby Get vratil started - to dela napr GPWebPay pri stisknuti Zpet
			}
		} else {
			$result = $payu->callGet($gwOrderNumber, $trans_data);
			
			if ($result==0) {
				$status = $trans_data['status'];
				 if (in_array($status, array(2,3,7,888))) {
					if ($transactionRecord->orderStatus != OrderStatus::$initiated && $transactionRecord->orderStatus != OrderStatus::$failedRetriable) {
						$orderStatus = OrderStatus::$failedFinal;
					} else {
						$orderStatus = OrderStatus::$failedRetriable; 
					}
				} else if (in_array($status,array(99,5))) {
					$orderStatus = OrderStatus::$successful;
				} else if (in_array($status, array(1))) {
					if ($isNotification) {
						$orderStatus = OrderStatus::$invalidReply;  // started v notifikaci ignoruju, protoze prijde prakticky vzdy pri zahajeni platby
					} else {
						$orderStatus = OrderStatus::$pending;  // v reply je to pending, typicky pro platbu prevodem
					}
				} else if (in_array($status, array(4))) {
					$orderStatus = OrderStatus::$pending;
				}
			} else {
				$orderStatus = OrderStatus::$failedRetriable; // to je spis nejaka systemova chyba, obvykle zpusobena ze je pre_error a tudiz transakce neexistuje, takze retriable
			}
		}
		
		$orderReplyStatus = new OrderReplyStatus();
		$orderReplyStatus->orderStatus = $orderStatus;
		$orderReplyStatus->resultText = null; //preklad atp
		$orderReplyStatus->gwOrderNumber = $gwOrderNumber;
		$orderReplyStatus->shopOrderNumber = $transactionRecord->shopOrderNumber;
		$orderReplyStatus->shopPairingInfo = $transactionRecord->shopPairingInfo;
		$orderReplyStatus->forexNote = $transactionRecord->forexNote;
		if ($orderReplyStatus->orderStatus != OrderStatus::$invalidReply) {
			$this->updateOrderReplyInDb($orderReplyStatus);
		}
		EndSynchronized();
		$orderReplyStatus->uniAdapterData = $transactionRecord->uniAdapterData;
		return $orderReplyStatus;
	}

	function getPayuConfig($currency) {
		$payuConfig = array(
			'encoding'=>'UTF',
			'log_file'=>$this->logger->logFile,
			'url_payu'=>$this->config->url_payu
		);
		
		if ($this->config->supportedCurrency == $currency) {
			$payuConfig['pos_id'] = $this->config->pos_id;
			$payuConfig['pos_auth_key'] = $this->config->pos_auth_key;
			$payuConfig['key1'] = $this->config->key1;
			$payuConfig['key2'] = $this->config->key2;
		} else if ($this->config->supportedCurrency_pos2 == $currency) {
			$payuConfig['pos_id'] = $this->config->pos_id_pos2;
			$payuConfig['pos_auth_key'] = $this->config->pos_auth_key_pos2;
			$payuConfig['key1'] = $this->config->key1_pos2;
			$payuConfig['key2'] = $this->config->key2_pos2;
		} else {
			user_error('neznama mena');
		}
		return $payuConfig;
	}
	
	public function getInfoBoxData($uniAdapterName, $language) {
		$infoBoxData = parent::getInfoBoxData($uniAdapterName, $language);
		$infoBoxData->link = 'http://www.payu.cz';
		$infoBoxData->image = 'PayU_150x150_animated-banner.gif';
		return $infoBoxData;
	}

}

